#include <string.h>
#include <stdio.h>
#include "net.h"
#include "arp.h"
#include "ethernet.h"
/**
 * @brief 初始的arp包
 * 
 */
static const arp_pkt_t arp_init_pkt = {
    .hw_type16 = constswap16(ARP_HW_ETHER),
    .pro_type16 = constswap16(NET_PROTOCOL_IP),
    .hw_len = NET_MAC_LEN,
    .pro_len = NET_IP_LEN,
    .sender_ip = NET_IF_IP,
    .sender_mac = NET_IF_MAC,
    .target_mac = {0}};

/**
 * @brief arp地址转换表，<ip,mac>的容器
 * 
 */
map_t arp_table;

/**
 * @brief arp buffer，<ip,buf_t>的容器
 * 
 */
map_t arp_buf;

/**
 * @brief 打印一条arp表项
 * 
 * @param ip 表项的ip地址
 * @param mac 表项的mac地址
 * @param timestamp 表项的更新时间
 */
void arp_entry_print(void *ip, void *mac, time_t *timestamp)
{
    printf("%s | %s | %s\n", iptos(ip), mactos(mac), timetos(*timestamp));
}

/**
 * @brief 打印整个arp表
 * 
 */
void arp_print()
{
    printf("===ARP TABLE BEGIN===\n");
    map_foreach(&arp_table, arp_entry_print);
    printf("===ARP TABLE  END ===\n");
}

/**
 * @brief 发送一个arp请求
 * 
 * @param target_ip 想要知道的目标的ip地址
 */
void arp_req(uint8_t *target_ip)
{
    // TO-DO
    buf_init(&txbuf,sizeof(arp_pkt_t));
    arp_pkt_t *apt = (arp_pkt_t *)txbuf.data;
    apt->hw_type16 = arp_init_pkt.hw_type16;
    apt->hw_len = arp_init_pkt.hw_len;
    apt->pro_type16 = arp_init_pkt.pro_type16;
    apt->pro_len = arp_init_pkt.pro_len;
    memcpy(apt->sender_ip,arp_init_pkt.sender_ip,NET_IP_LEN);
    memcpy(apt->sender_mac,arp_init_pkt.sender_mac,NET_MAC_LEN);
    memcpy(apt->target_mac,arp_init_pkt.target_mac,NET_MAC_LEN);
    memcpy(apt->target_ip,target_ip,NET_IP_LEN);
    apt->opcode16 = swap16(ARP_REQUEST);
    uint8_t dstmac[NET_MAC_LEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
    ethernet_out(&txbuf,dstmac,NET_PROTOCOL_ARP);
    return;
}

/**
 * @brief 发送一个arp响应
 * 
 * @param target_ip 目标ip地址
 * @param target_mac 目标mac地址
 */
void arp_resp(uint8_t *target_ip, uint8_t *target_mac)
{
    // TO-DO
    buf_init(&txbuf,sizeof(arp_pkt_t));
    //buf_add_header(&txbuf,sizeof(arp_pkt_t));
    arp_pkt_t *apt = (arp_pkt_t *)txbuf.data;
    apt->hw_type16 = arp_init_pkt.hw_type16;
    apt->hw_len = arp_init_pkt.hw_len;
    apt->pro_type16 = arp_init_pkt.pro_type16;
    apt->pro_len = arp_init_pkt.pro_len;
    memcpy(apt->sender_ip,arp_init_pkt.sender_ip,NET_IP_LEN);
    memcpy(apt->sender_mac,arp_init_pkt.sender_mac,NET_MAC_LEN);
    memcpy(apt->target_mac,target_mac,NET_MAC_LEN);
    memcpy(apt->target_ip,target_ip,NET_IP_LEN);
    apt->opcode16 = swap16(ARP_REPLY);
    ethernet_out(&txbuf,target_mac,NET_PROTOCOL_ARP);
}

/**
 * @brief 处理一个收到的数据包
 * 
 * @param buf 要处理的数据包
 * @param src_mac 源mac地址
 */
void arp_in(buf_t *buf, uint8_t *src_mac)
{
    // TO-DO
    arp_pkt_t *apt = (arp_pkt_t *)buf->data;
    if(buf->len < sizeof(arp_pkt_t)){
        return;
    }
    /*apt->hw_type16 = srcapt->hw_type16;
    apt->hw_len = srcapt->hw_len;
    apt->pro_type16 = srcapt->pro_type16;
    apt->pro_len = srcapt->pro_len;
    memcpy(apt->sender_ip,srcapt->sender_ip,NET_IP_LEN);
    memcpy(apt->sender_mac,srcapt->sender_mac,NET_MAC_LEN);
    memcpy(apt->target_mac,srcapt->target_mac,NET_MAC_LEN);
    memcpy(apt->target_ip,srcapt->sender_ip,NET_IP_LEN);
    apt->opcode16 = srcapt->opcode16;*/
    //buf_remove_header(buf,sizeof(arp_pkt_t));
    if(apt->hw_type16 != arp_init_pkt.hw_type16 || apt->pro_type16 != arp_init_pkt.pro_type16 || 
    apt->hw_len != arp_init_pkt.hw_len || apt->pro_len != arp_init_pkt.pro_len || (swap16(apt->opcode16) != ARP_REPLY && swap16(apt->opcode16) != ARP_REQUEST)){
        return;
    }
    map_set(&arp_table,apt->sender_ip,src_mac);
    buf_t *dstbuf = map_get(&arp_buf,apt->sender_ip);
    if(dstbuf != NULL){
        ethernet_out(dstbuf,apt->sender_mac,NET_PROTOCOL_IP);
        map_delete(&arp_buf,apt->sender_ip);
        return;
    }else{
        uint8_t selfIP[NET_IP_LEN] = NET_IF_IP;
        if(swap16(apt->opcode16) == ARP_REQUEST && memcmp(apt->target_ip,selfIP,sizeof(selfIP)) == 0){
            arp_resp(apt->sender_ip,apt->sender_mac);
        }
    }

}

/**
 * @brief 处理一个要发送的数据包
 * 
 * @param buf 要处理的数据包
 * @param ip 目标ip地址
 * @param protocol 上层协议
 */
void arp_out(buf_t *buf, uint8_t *ip)
{
    // TO-DO
    uint8_t* dstmac = (uint8_t *)map_get(&arp_table,ip);
    if(dstmac!= NULL){
        ethernet_out(buf,dstmac,NET_PROTOCOL_IP);
        return;
    }
    //buf_t *dstbuf = map_get(&arp_buf,ip);
    if(map_get(&arp_buf,ip) == NULL){
        map_set(&arp_buf,ip,buf);
        arp_req(ip);
        return;
    }
}

/**
 * @brief 初始化arp协议
 * 
 */
void arp_init()
{
    map_init(&arp_table, NET_IP_LEN, NET_MAC_LEN, 0, ARP_TIMEOUT_SEC, NULL);
    map_init(&arp_buf, NET_IP_LEN, sizeof(buf_t), 0, ARP_MIN_INTERVAL, buf_copy);
    net_add_protocol(NET_PROTOCOL_ARP, arp_in);
    arp_req(net_if_ip);
}